﻿using Daq.Tools.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace Daq.Tools.HttpClientHelper
{
    /// <summary>
    /// HTTP 基础使用
    /// </summary>
    public static class HttpHelp
    {
        /// <summary>
        /// HTTP配置
        /// </summary>
        private static HttpClientHandler handler => new HttpClientHandler()
        {
            ClientCertificateOptions = ClientCertificateOption.Manual,
            // 忽略SSL验证
            ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, certChain, policyErrors) => true,
            AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
            UseProxy = false
        };

        /// <summary>
        /// Get异步请求
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="getRequest">参数</param>
        /// <returns>请求的响应结果T类型</returns>
        /// <exception cref="BusinessException">请求错误</exception>
        public static async Task<T> GetAsync<T>(string url, Action<DaqGetRequest> getRequest = null)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, url);

            var param = new DaqGetRequest();
            if (getRequest.IsNotNull())
            {
                getRequest(param);
            }
            // 添加请求头
            foreach (var header in param.HeadersKv)
            {
                request.Headers.Add(header.Key, header.Value);
            }

            // 添加参数
            var urlParmam = string.Empty;
            foreach (var pair in param.ParamsKv)
            {
                urlParmam += $"&{pair.Key}={pair.Value}";
            }
            if (urlParmam.IsNotNullOrEmpty())
            {
                url += url.Contains("?") ? urlParmam : urlParmam.TrimStart('&').Insert(0, "?");
            }

            using (var client = new HttpClient(handler))
            {
                if (client.Timeout != TimeSpan.FromSeconds(param.TimeoutSecond))
                {
                    client.Timeout = TimeSpan.FromSeconds(param.TimeoutSecond);
                }
                var response = await client.SendAsync(request);
                if (response.IsSuccessStatusCode)
                {
                    var result = await response.Content.ReadAsStringAsync();
                    return typeof(T).Name == "String" ? (T)Convert.ChangeType(result, typeof(T)) : result.JsonToObj<T>();
                }
                else
                {
                    throw new BusinessException($"接口请求错误:{url},错误代码{response.StatusCode}，错误原因{response.ReasonPhrase}");
                }
            }
        }

        /// <summary>
        /// POST JSON异步请求
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="postRequest">参数</param>
        /// <param name="callBack">回到函数，提供发送的数据</param>
        /// <returns>请求的响应结果T类型</returns>
        /// <exception cref="BusinessException">请求错误</exception>
        public static async Task<T> PostJsonAsync<T>(string url, Action<DaqPostRequest> postRequest = null, Action<string> callBack = null)
        {
            var param = new DaqPostRequest();
            if (postRequest.IsNotNull())
            {
                postRequest(param);
            }

            var requestContent = new StringContent(param.JsonData, Encoding.UTF8, "application/json");

            // 添加请求头
            foreach (var header in param.HeadersKv)
            {
                requestContent.Headers.Add(header.Key, header.Value);
            }

            using (var client = new HttpClient(handler))
            {
                if (client.Timeout != TimeSpan.FromSeconds(param.TimeoutSecond))
                {
                    client.Timeout = TimeSpan.FromSeconds(param.TimeoutSecond);
                }

                if (callBack.IsNotNull())
                {
                    _ = Task.Run(() =>
                    {
                        callBack(requestContent.ToJson());
                    });
                }

                var response = await client.PostAsync(url, requestContent);
                if (response.IsSuccessStatusCode)
                {
                    var result = await response.Content.ReadAsStringAsync();
                    return typeof(T).Name == "String" ? (T)Convert.ChangeType(result, typeof(T)) : result.JsonToObj<T>();
                }
                else
                {
                    throw new BusinessException($"接口请求错误:{url},错误代码{response.StatusCode}，错误原因{response.ReasonPhrase}");
                }
            }
        }

        /// <summary>
        /// POST 表单异步请求
        /// multipart/form-data
        /// </summary>
        /// <typeparam name="T">返回的数据类型</typeparam>
        /// <param name="url">请求地址</param>
        /// <param name="mutipartRequest">参数</param>
        /// <returns>请求的响应结果T类型</returns>
        /// <exception cref="BusinessException">请求错误</exception>
        public static async Task<T> PostFormDataAsync<T>(string url, Action<DaqMultipartRequest> mutipartRequest = null)
        {
            var param = new DaqMultipartRequest();
            if (mutipartRequest.IsNotNull())
            {
                mutipartRequest(param);
            }

            // 组装参数
            var form = new MultipartFormDataContent();

            string boundary = string.Format("--{0}", DateTime.Now.Ticks.ToString("x"));
            form.Headers.Add("ContentType", $"multipart/form-data, boundary=--{DateTime.Now.Ticks:x}");

            // 添加请求头
            foreach (var header in param.HeadersKv)
            {
                form.Headers.Add(header.Key, header.Value);
            }
            // 发送的键值数据
            foreach (var kv in param.ParamsKv)
            {
                form.Add(new StringContent(kv.Value, Encoding.UTF8), kv.Key);
            };
            // 发送的文件
            foreach (var kv in param.FilesKv)
            {
                form.Add(new StreamContent(kv.Value.Item2), kv.Key, kv.Value.Item1);
            }

            using (var client = new HttpClient(handler))
            {
                if (client.Timeout != TimeSpan.FromSeconds(param.TimeoutSecond))
                {
                    client.Timeout = TimeSpan.FromSeconds(param.TimeoutSecond);
                }
                HttpResponseMessage response;
                try
                {
                    response = await client.PostAsync(url, form);
                    if (response.IsSuccessStatusCode)
                    {
                        var result = await response.Content.ReadAsStringAsync();
                        return typeof(T).Name == "String" ? (T)Convert.ChangeType(result, typeof(T)) : result.JsonToObj<T>();
                    }
                    else
                    {
                        throw new BusinessException($"接口请求错误:{url},错误代码{response.StatusCode}，错误原因{response.ReasonPhrase}");
                    }
                }
                catch
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// POST 表单异步请求
        /// x-www-form-urlencoded
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="url"></param>
        /// <param name="mutipartRequest"></param>
        /// <returns></returns>
        public static async Task<T> PostUrlEncodedAsync<T>(string url, Action<DaqMultipartRequest> mutipartRequest = null)
        {
            Dictionary<string, string> data = new Dictionary<string, string>();

            var param = new DaqMultipartRequest();
            if (mutipartRequest.IsNotNull())
            {
                mutipartRequest(param);
                data = param.ParamsKv;
            }

            var formContent = new FormUrlEncodedContent(data);
            formContent.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

            // 添加请求头
            foreach (var header in param.HeadersKv)
            {
                formContent.Headers.Add(header.Key, header.Value);
            }

            using (var client = new HttpClient(handler))
            {
                if (client.Timeout != TimeSpan.FromSeconds(param.TimeoutSecond))
                {
                    client.Timeout = TimeSpan.FromSeconds(param.TimeoutSecond);
                }

                HttpResponseMessage response;
                try
                {
                    response = await client.PostAsync(url, formContent);
                    if (response.IsSuccessStatusCode)
                    {
                        var result = await response.Content.ReadAsStringAsync();
                        return typeof(T).Name == "String" ? (T)Convert.ChangeType(result, typeof(T)) : result.JsonToObj<T>();
                    }
                    else
                    {
                        throw new BusinessException($"接口请求错误:{url},错误代码{response.StatusCode}，错误原因{response.ReasonPhrase}");
                    }
                }
                catch
                {
                    throw;
                }
            }
        }

        /// <summary>
        /// 下载文件到到Stream
        /// </summary>
        /// <param name="url">下载的地址</param>
        /// <param name="getRequest">参数</param>
        /// <returns>T1：下载的文件流，T2：下载的文件名</returns>
        /// <exception cref="BusinessException">请求错误</exception>
        public static async Task<(Stream, string)> DownFileToStream(string url, Action<DaqGetRequest> getRequest = null)
        {
            var param = new DaqGetRequest();
            if (getRequest.IsNotNull())
            {
                getRequest(param);
            }

            var request = new HttpRequestMessage(HttpMethod.Get, url);
            // 添加请求头
            foreach (var header in param.HeadersKv)
            {
                request.Headers.Add(header.Key, header.Value);
            }

            // 添加参数
            var urlParmam = string.Empty;
            foreach (var pair in param.ParamsKv)
            {
                urlParmam += $"&{pair.Key}={pair.Value}";
            }
            if (urlParmam.IsNotNullOrEmpty())
            {
                url += url.Contains("?") ? urlParmam : urlParmam.TrimStart('&').Insert(0, "?");
            }

            using (var client = new HttpClient(handler))
            {
                if (client.Timeout != TimeSpan.FromSeconds(param.TimeoutSecond))
                {
                    client.Timeout = TimeSpan.FromSeconds(param.TimeoutSecond);
                }
                var response = await client.SendAsync(request);
                if (response.IsSuccessStatusCode)
                {
                    string filename = response.Content.Headers.ContentDisposition.FileNameStar;
                    var stream = await response.Content.ReadAsStreamAsync();
                    return (stream, filename);
                }
                else
                {
                    throw new BusinessException($"接口请求错误:{url},错误代码{response.StatusCode}，错误原因{response.ReasonPhrase}");
                }
            }
        }

        /// <summary>
        /// 下载文件到到本地
        /// </summary>
        /// <param name="url">下载的地址</param>
        /// <param name="saveFilePath">保存的本地路径，包括文件名称</param>
        /// <param name="getRequest">参数</param>
        /// <returns>T1：下载的文件流，T2：下载的文件名</returns>
        /// <exception cref="BusinessException">请求错误</exception>
        public static async Task<bool> DownFileToLocal(string url, string saveFilePath, Action<DaqGetRequest> getRequest = null)
        {
            if (string.IsNullOrWhiteSpace(saveFilePath))
            {
                throw new BusinessException("请提供文件下载到本地的路径");
            }
            Directory.CreateDirectory(Path.GetDirectoryName(saveFilePath));
            var (stream, fileName) = await DownFileToStream(url, getRequest);

            using (var fs = File.Create(saveFilePath))
            {
                stream.Seek(0, SeekOrigin.Begin);
                await stream.CopyToAsync(fs);
                await stream.FlushAsync();

                return true;
            }
        }
    }
}